﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using LitJson;

[AddComponentMenu("PUEX/Affector/Velocity/RandomiserAffector")]
public class PUEXRandomiserAffector : PUEXAffector {

	#region - Editable property
	public Vector3 center;
	public string centerParamName = "";
	public Vector3 size;
	public string sizeParamName = "";
	public PUEXDynamicAttribute deviationX;
	public PUEXDynamicAttribute deviationY;
	public PUEXDynamicAttribute deviationZ;
	public bool useEmitterTime;
	public bool inWorldSpace;
	public bool applyOwnerScale;
	public float timeStep;
	public bool randomVelocity;
	public bool randomPosition;
	#endregion
	
	#region - Property
	protected float _timeSinceLastUpdate;
	protected bool _needUpdate;
	protected Bounds _bounds;
	#endregion
	
	#region - Lifecycle
	public PUEXRandomiserAffector ()
	{
		center = Vector3.zero;
		size = new Vector3 (20f, 20f, 20f);
		deviationX = PUEXDynamicAttribute.GenerateFixed(3f);
		deviationY = PUEXDynamicAttribute.GenerateFixed(3f);
		deviationZ = PUEXDynamicAttribute.GenerateFixed(3f);
		useEmitterTime = false;
		inWorldSpace = true;
		applyOwnerScale = true;
		timeStep = 0.1f;
		randomVelocity = true;
		randomPosition = false;
	}

	public override void enable () {
		_timeSinceLastUpdate = 0f;
		_needUpdate = false;
		
		base.enable ();
	}
	#endregion
	
	#region - Update
	public override void preUpdate (float dt)
	{
		base.preUpdate (dt);

		_timeSinceLastUpdate += dt;
		if (_timeSinceLastUpdate > timeStep)
		{
			_timeSinceLastUpdate -= timeStep;
			_needUpdate = true;

			Vector3 center_ = center;
			Vector3 size_ = size;
			if (inWorldSpace) {
				if (ownerEmitter.keepLocal) {
					Matrix4x4 invMat = ownerEmitter.ownerParticleSystem.systemToWorldTransform.inverse;
					center_ = invMat.MultiplyPoint(center_);
					Vector3 s = ownerEmitter.ownerParticleSystem.systemToWorldScale3;
					s = new Vector3 (1f / s.x, 1f / s.y, 1f / s.z);
					size_.Scale(s);
				}
			} else {
				if (!ownerEmitter.keepLocal) {
					center_ = ownerEmitter.ownerParticleSystem.systemToWorldTransform.MultiplyPoint(center_);
					size_.Scale(ownerEmitter.ownerParticleSystem.systemToWorldScale3);
				}
			}
			
			_bounds = new Bounds (center_, size_);
		}
	}

	public override void update (float dt, PUEXParticle particle)
	{
		if (!_needUpdate)
			return;

		float t = particle.timeFraction;
		if (useEmitterTime)
			t = inTrigger () ? triggerTimeFraction : ownerEmitter.timeFraction;

		if (_bounds.Contains (particle.position)) {
			float dx = deviationX.getValue(t);
			float dy = deviationY.getValue(t);
			float dz = deviationZ.getValue(t);
			if (applyOwnerScale) {
				dx *= ownerEmitter.ownerParticleSystem.systemToWorldScale3.x;
				dy *= ownerEmitter.ownerParticleSystem.systemToWorldScale3.y;
				dz *= ownerEmitter.ownerParticleSystem.systemToWorldScale3.z;
			}

			if (randomVelocity)
			{
				particle.setVelocity(particle.velocity() + new Vector3(Random.Range (-dx, dx),
                      													Random.Range (-dy, dy),
                      													Random.Range (-dz, dz)));
			}
			if (randomPosition && !particle.freezed)
			{
				particle.position = particle.position + new Vector3(Random.Range (-dx, dx),
				                                					Random.Range (-dy, dy),
				                                                    Random.Range (-dz, dz));
			}
		}
	}

	public override void postUpdate (float dt)
	{
		_needUpdate = false;

		base.postUpdate (dt);
	}
	#endregion
	
	#region - Param property
	public override void updateParams (PUEXParticleSystem system)
	{
		Vector3 value;
		if (system.getParamVec3 (centerParamName, out value)) {
			center = value;
		}
		if (system.getParamVec3 (sizeParamName, out value)) {
			size = value;
		}
		deviationX.parameterize (system);
		deviationY.parameterize (system);
		deviationZ.parameterize (system);
	}
	#endregion
	
	#region - Draw
	public void OnDrawGizmos()
	{
		if (!enabled)
			return;
		
		Matrix4x4 defaultMatrix = Gizmos.matrix;
		if (inWorldSpace) {
			Gizmos.matrix = Matrix4x4.identity;
		} else {
			Gizmos.matrix = transform.parent.transform.localToWorldMatrix;
		}
		
		Color defaultColor = Gizmos.color;
		Gizmos.color = Color.red;
		
		Gizmos.DrawWireCube (center, size);
		
		Gizmos.color = defaultColor;
		Gizmos.matrix = defaultMatrix;
	}
	#endregion

	#region - Serialization
	public override JsonData save (JsonData emitterData, JsonData systemData)
	{
		JsonData ret = base.save (emitterData, systemData);

		ret ["center"] = PUEXUtils.serializeVector3 (center);
		ret ["center_param_name"] = centerParamName;
		ret ["size"] = PUEXUtils.serializeVector3 (size);
		ret ["size_param_name"] = sizeParamName;
		ret ["deviation_x"] = PUEXUtils.serializeDynamicAttribute(deviationX);
		ret ["deviation_y"] = PUEXUtils.serializeDynamicAttribute(deviationY);
		ret ["deviation_z"] = PUEXUtils.serializeDynamicAttribute(deviationZ);
		ret ["use_emitter_time"] = useEmitterTime;
		ret ["in_world_space"] = inWorldSpace;
		ret ["apply_owner_scale"] = applyOwnerScale;
		ret ["time_step"] = timeStep;
		ret ["random_velocity"] = randomVelocity;
		ret ["random_position"] = randomPosition;
		
		return ret;
	}
	#endregion

}
